package com.trinh.utils.crypto.cryptors;

import com.trinh.utils.crypto.config.KeyConfig;
import com.trinh.utils.crypto.enu.CharFormater;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @Author: TrinhRae
 * @Package: com.trinh.utils.crypto.cryptors
 * @Project: generaltools
 * @Date: 2025/6/24 9:30
 */
public class SM2Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ALGORITHM_NAME = "EC";
    private static final String PROVIDER = "BC";

    private KeyConfig keyConfig;
    private CharFormater charFormater;

    public SM2Util(KeyConfig keyConfig) {
        this.keyConfig = keyConfig;
        this.charFormater = CharFormater.BASE64;
    }

    public SM2Util(KeyConfig keyConfig, CharFormater charFormater) {
        this.keyConfig = keyConfig;
        this.charFormater = charFormater;
    }

    /**
     * 生成 SM2 密钥对
     */
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_NAME, PROVIDER);
        keyPairGenerator.initialize(new ECGenParameterSpec("sm2p256v1"), new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * SM2 加密
     */
    public String encrypt(String data) throws Exception {
        PublicKey publicKey = getPublicKeyFromBase64(keyConfig.getPublicKey());
        SM2Engine engine = new SM2Engine();
        ECPublicKeyParameters pubKeyParams = (ECPublicKeyParameters)
                ECUtil.generatePublicKeyParameter(publicKey);
        ParametersWithRandom parametersWithRandom = new ParametersWithRandom(pubKeyParams, new SecureRandom());
        engine.init(true, parametersWithRandom);
        byte[] encryptedData = engine.processBlock(data.getBytes(), 0, data.getBytes().length);
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * SM2 解密
     */
    public String decrypt(String cipherText) throws Exception {
        PrivateKey privateKey = getPrivateKeyFromBase64(keyConfig.getPrivateKey());
        SM2Engine engine = new SM2Engine();
        ECPrivateKeyParameters priKeyParams = (ECPrivateKeyParameters)
                ECUtil.generatePrivateKeyParameter(privateKey);
        engine.init(false, priKeyParams);
        byte[] decryptedData = engine.processBlock(Base64.getDecoder().decode(cipherText), 0,
                Base64.getDecoder().decode(cipherText).length);
        return new String(decryptedData);
    }

    /**
     * 从 Base64 编码恢复公钥对象
     */
    public PublicKey getPublicKeyFromBase64(String publicKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        return KeyFactory.getInstance("EC", "BC").generatePublic(keySpec);
    }

    /**
     * 从 Base64 编码恢复私钥对象
     */
    public PrivateKey getPrivateKeyFromBase64(String privateKey) throws Exception {
        byte[] keyBytes = charFormater.getTool().toBytes(privateKey);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        return KeyFactory.getInstance("EC", "BC").generatePrivate(keySpec);
    }

    /**
     * SM2 签名（使用 SM3 摘要）
     */
    public String sign(String data) throws Exception {
        PrivateKey privateKey = getPrivateKeyFromBase64(keyConfig.getPrivateKey());
        SM2Signer signer = new SM2Signer();
        ECPrivateKeyParameters priKeyParams = (ECPrivateKeyParameters)
                ECUtil.generatePrivateKeyParameter(privateKey);
        ParametersWithRandom params = new ParametersWithRandom(priKeyParams, new SecureRandom());
        signer.init(true, params);
        signer.update(data.getBytes(), 0, data.getBytes().length);
        byte[] signature = signer.generateSignature();
        return Base64.getEncoder().encodeToString(signature);
    }

    /**
     * SM2 验签
     */
    public boolean verify(String data, String signature) throws Exception {
        PublicKey publicKey = getPublicKeyFromBase64(keyConfig.getPublicKey());
        SM2Signer verifier = new SM2Signer();
        ECPublicKeyParameters pubKeyParams = (ECPublicKeyParameters)
                ECUtil.generatePublicKeyParameter(publicKey);
        verifier.init(false, pubKeyParams);
        verifier.update(data.getBytes(), 0, data.getBytes().length);
        return verifier.verifySignature(Base64.getDecoder().decode(signature));
    }

}
